package com.xiaoyy.core.config.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoyy.core.common.exception.AppException;
import com.xiaoyy.core.common.vo.ModifiedFieldVO;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 相同实体类字段比较工具类
 * @author chenzh
 */
public class FieldChangeUtil {
    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     * Compare two object and return modified fields
     *
     * @param source source object
     * @param target target object
     * @return the modified fields and value after modify
     */
    public static List<ModifiedFieldVO> getModifyContent(Object source, Object target) {
        List<ModifiedFieldVO> list = new ArrayList<>();
         /*
          process null problem, if all null means equal
          if only source is null means all modified
          if only target is null means nothing changed
         */
        if (null == source || null == target) {
            return list;
        }
        // source and target must be same class type
        if (!Objects.equals(source.getClass().getName(), target.getClass().getName())) {
            throw new AppException("不是相同的类");
        }
        Map<String, Object> sourceMap = mapper.convertValue(source, new TypeReference<Map<String, Object>>() {
        });
        Map<String, Object> targetMap = mapper.convertValue(target, new TypeReference<Map<String, Object>>() {
        });
        sourceMap.forEach((k, v) -> {
            Object targetValue = targetMap.get(k);
            if (v == null) {
                v = "";
            } else {
                v = v.toString().trim();
            }
            if (targetValue == null) {
                targetValue = "";
            } else {
                targetValue = targetValue.toString().trim();
            }
            setFieldVOList(k,v,targetValue,list);
        });
        return list;
    }

    /**
     * Compare two object and return modified fields which contain in comparedProperties
     *
     * @param source             ource object
     * @param target             target object
     * @param comparedProperties the fields need to be compare
     * @return the modified fields and value after modify
     */
    public static List<ModifiedFieldVO> getModifyContent(Object source, Object target, Map<String, String> comparedProperties) {
        List<ModifiedFieldVO> list = new ArrayList<>();
        if (null == source || null == target) {
            return list;
        }
        if (!Objects.equals(source.getClass().getName(), target.getClass().getName())) {
            throw new AppException("不是相同的类");
        }
        Map<String, Object> sourceMap = mapper.convertValue(source, new TypeReference<Map<String, Object>>() {
        });
        Map<String, Object> targetMap = mapper.convertValue(target, new TypeReference<Map<String, Object>>() {
        });
        sourceMap.forEach((k, v) -> {
            if (comparedProperties != null && !comparedProperties.containsKey(k)) {
                return;
            }
            Object targetValue = targetMap.get(k);
            setFieldVOList(k,v,targetValue,list);
        });
        return list;
    }

    /***
     * 剔除字段比对两个实体的不同
     * */
    public static List<ModifiedFieldVO> getModifyContentRemove(Object source, Object target, Map<String, String> removeProperties) {
        List<ModifiedFieldVO> list = new ArrayList<>();
        if (null == source || null == target) {
            return list;
        }
        if (!Objects.equals(source.getClass().getName(), target.getClass().getName())) {
            throw new AppException("不是相同的类");
        }
        Map<String, Object> sourceMap = mapper.convertValue(source, new TypeReference<Map<String, Object>>() {
        });
        Map<String, Object> targetMap = mapper.convertValue(target, new TypeReference<Map<String, Object>>() {
        });
        sourceMap.forEach((k, v) -> {
            if (removeProperties != null && removeProperties.containsKey(k)) {
                return;
            }
            Object targetValue = targetMap.get(k);
            setFieldVOList(k,v,targetValue,list);
        });
        return list;
    }
    public static void setFieldVOList(String k,Object v,Object targetValue,List<ModifiedFieldVO> list){
        if (!Objects.equals(v, targetValue)) {
            ModifiedFieldVO modifiedFieldVO = new ModifiedFieldVO();
            modifiedFieldVO.setField(k);
            if (v != null) {
                modifiedFieldVO.setBeforeValue(String.valueOf(v));
            }
            if (targetValue != null) {
                modifiedFieldVO.setAfterValue(String.valueOf(targetValue));
            }
            list.add(modifiedFieldVO);
        }
    }

}

